package com.xh.utils;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.xh.common.consts.RConst;

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Objects;

/**
 * @author wen
 * @apiNote
 * @since 2022/1/13
 */
public class DateUtil {

    public static final String DEFAULT_PATTERN = "yyyy-MM-dd HH:mm:ss";
    public static final String DATE_PATTERN = "yyyyMMddHHmmssSSS";
    public static final long DAY_MILLION_SECOND = 24 * 60 * 60 * 1000L;
    public static final long HOUR_MILLION_SECOND = 60 * 60 * 1000L;
    public static final long MINUTE_MILLION_SECOND = 60 * 1000L;

    /**
     * 初始化时间格式
     */
    public static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern(DEFAULT_PATTERN);
    public static final DateTimeFormatter FORMATTER_1 = DateTimeFormatter.ofPattern("MM月dd日 HH:mm:ss");

    /**
     * 初始化时间格式 yyyy-MM-dd
     */
    public static final DateTimeFormatter FORMATTER_Y_M_D = DateTimeFormatter.ofPattern("yyyy-MM-dd");

    public static final DateTimeFormatter FORMATTER_Y_M_D_H_M_S = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    public static final DateTimeFormatter FORMATTER_Y_M_D_H_M = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");

    /**
     * 初始化时间格式 HH:mm
     */
    public static final DateTimeFormatter FORMATTER_H_M = DateTimeFormatter.ofPattern("HH:mm");

    /**
     * 10天的毫秒值
     */
    public static final Long TEN_DAY = 10 * 24 * 60 * 60 * 1000L;

    /**
     * @apiNote 获取当前格式化时间
     * @author wen
     * @since 2022/1/13
     */
    public static String getNowDateTime() {
        return LocalDateTime.now().format(FORMATTER);
    }

    /**
     * 毫秒值转LocalDateTime
     */
    public static LocalDateTime longToLocalDateTime(Long milliSecond) {
        if (Objects.isNull(milliSecond)) {
            throw new RuntimeException("时间戳不能为空");
        }
        return new Date(milliSecond).toInstant().atOffset(RConst.Time.ZONE_OFF_SET_8).toLocalDateTime();
    }

    /**
     * 毫秒值转LocalDateTime
     */
    public static String longToString(Long milliSecond) {
        return longToLocalDateTime(milliSecond).format(FORMATTER_1);
    }

    /**
     * 格式化时间为字符串
     *
     * @param localDateTime 传参
     * @return {@link String}
     */
    public static String formatDate(LocalDateTime localDateTime) {
        if (ObjectUtil.isEmpty(localDateTime)) return null;
        return localDateTime.format(FORMATTER);
    }

    /**
     * 格式化时间为字符串
     *
     * @param localTime 传参
     * @return {@link String}
     */
    public static String formatDate(LocalTime localTime, String pattern) {
        if (ObjectUtil.isEmpty(localTime)) return null;
        return localTime.format(DateTimeFormatter.ofPattern(pattern));
    }

    public static String formatDate(LocalDateTime localDateTime, DateTimeFormatter dateTimeFormatter) {
        if (ObjectUtil.isEmpty(localDateTime)) return null;
        return localDateTime.format(dateTimeFormatter);
    }

    /**
     * 得到日期字符串 默认格式（yyyy-MM-dd） pattern可以为："yyyy-MM-dd" "HH:mm:ss" "E"
     */
    public static String formatDate(Date date, Object... pattern) {
        String formatDate;
        if (pattern != null && pattern.length > 0) {
            formatDate = cn.hutool.core.date.DateUtil.format(date, pattern[0].toString());
        } else {
            formatDate = cn.hutool.core.date.DateUtil.format(date, "yyyy-MM-dd");
        }
        return formatDate;
    }

    /**
     * 格式化时间为字符串
     *
     * @param date 传参
     * @return {@link String}
     */
    public static LocalDateTime dateToLocalDateTime(Date date) {
        if (ObjectUtil.isEmpty(date)) return null;
        return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
    }

    /**
     * 计算当天开始时间或者结束时间
     */
    public static Long getStartOrEndTime(Boolean isStart) {
        //时间戳固定 可以转换成对应时间区的对应时间
        return LocalDate.now().atTime(isStart ? LocalTime.MIN : LocalTime.MAX)
                .atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
    }

    /**
     * LocalDateTime转毫秒值
     */
    public static Long localDateTimeToLong(LocalDateTime localDateTime) {
        if (Objects.isNull(localDateTime)) return null;
        return localDateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
    }

    /**
     * 计算现在时间次数 * 十天 的时间
     * <p>
     * isStart true 开始时间 false 结束时间
     */
    public static LocalDateTime getTenDayStartOrEndTime(Long count, Boolean isStart) {
        //时间戳固定 可以转换成对应时间区的对应时间
        long time = LocalDate.now().atTime(isStart ? LocalTime.MIN : LocalTime.MAX)
                .atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
        return longToLocalDateTime(time - (count + 1) * TEN_DAY);
    }

    /**
     * 当天开始
     **/
    public static Date getTodayStart() {
        Calendar todayStart = Calendar.getInstance();
        todayStart.set(Calendar.HOUR, 0);
        todayStart.set(Calendar.MINUTE, 0);
        todayStart.set(Calendar.SECOND, 0);
        todayStart.set(Calendar.MILLISECOND, 0);
        return todayStart.getTime();
    }

    /**
     * 当天结束
     **/
    public static Date getTodayEnd() {
        Calendar todayEnd = Calendar.getInstance();
        todayEnd.set(Calendar.HOUR, 23);
        todayEnd.set(Calendar.MINUTE, 59);
        todayEnd.set(Calendar.SECOND, 59);
        todayEnd.set(Calendar.MILLISECOND, 999);
        return todayEnd.getTime();
    }

    /**
     * 查询所传毫秒值内包含多少天/时/分
     */
    public static long getMillionSecondByNum(long time, int type) {
        long millionSecond;
        switch (type) {
            case 1:
                millionSecond = DAY_MILLION_SECOND;
                break;
            case 2:
                millionSecond = HOUR_MILLION_SECOND;
                break;
            case 3:
                millionSecond = MINUTE_MILLION_SECOND;
                break;
            default:
                throw new RuntimeException("所传type无对应处理方案");
        }
        return time * millionSecond;
    }

    /**
     * 查询所传毫秒值内包含多少天/时/分
     */
    public static long getNumByMillionSecond(long time, int type) {
        long millionSecond;
        long count;
        switch (type) {
            case 1:
                millionSecond = DAY_MILLION_SECOND;
                break;
            case 2:
                millionSecond = HOUR_MILLION_SECOND;
                count = time / DAY_MILLION_SECOND;
                time = time - count * DAY_MILLION_SECOND;
                break;
            case 3:
                millionSecond = MINUTE_MILLION_SECOND;
                count = time / DAY_MILLION_SECOND;
                time = time - count * DAY_MILLION_SECOND;
                count = time / HOUR_MILLION_SECOND;
                time = time - count * HOUR_MILLION_SECOND;
                break;
            default:
                throw new RuntimeException("所传type无对应处理方案");
        }
        return time / millionSecond;
    }

    /**
     * 将 LocalDate 转为时间戳（秒）
     *
     * @return 时间戳（秒）
     */
    public static Long localDateToSeconds(LocalDate localDate) {
        if (ValidUtil.isNull(localDate)) {
            return null;
        }
        return localDate.atStartOfDay(ZoneOffset.ofHours(8)).toInstant().getEpochSecond();
    }

    /**
     * 将 LocalDate 转为时间戳（毫秒）
     *
     * @return 时间戳（毫秒）
     */
    public static Long localDateToMills(LocalDate localDate) {
        if (ValidUtil.isNull(localDate)) {
            return null;
        }
        return localDate.atStartOfDay(ZoneOffset.ofHours(8)).toInstant().toEpochMilli();
    }


    /**
     * 将 LocalDateTime 转为时间戳（秒）
     *
     * @return 时间戳（秒）
     */
    public static Long localDateTimeToSeconds(LocalDateTime localDateTime) {
        if (ValidUtil.isNull(localDateTime)) {
            return null;
        }
        return localDateTime.toEpochSecond(ZoneOffset.ofHours(8));
    }

    /**
     * 将 LocalDateTime 转为时间戳（毫秒）
     *
     * @param localDateTime
     * @return 时间戳（毫秒）
     */
    public static Long localDateTimeToMills(LocalDateTime localDateTime) {
        if (ValidUtil.isNull(localDateTime)) {
            return RConst.Num.BASE_LONG_0;
        }
        return localDateTime.toInstant(ZoneOffset.ofHours(8)).toEpochMilli();
    }

    /**
     * 将时间戳(秒)转为 LocalDate
     *
     * @param seconds 时间戳（秒）
     * @return LocalDate
     */
    public static LocalDate secondsToLocalDate(Long seconds) {
        if (ValidUtil.isNull(seconds)) {
            return null;
        }
        return Instant.ofEpochSecond(seconds).atZone(ZoneOffset.ofHours(8)).toLocalDate();
    }

    /**
     * 将时间戳(毫秒)转为 LocalDate
     *
     * @param milliseconds 时间戳（毫秒）
     * @return LocalDate
     */
    public static LocalDate millsToLocalDate(Long milliseconds) {
        if (ValidUtil.isNull(milliseconds)) {
            return null;
        }
        return Instant.ofEpochMilli(milliseconds).atZone(ZoneOffset.ofHours(8)).toLocalDate();
    }

    /**
     * 将时间戳(秒)转为 LocalDateTime
     *
     * @param seconds 时间戳（秒）
     * @return LocalDateTime
     */
    public static LocalDateTime secondsToLocalDateTime(Long seconds) {
        if (ValidUtil.isNull(seconds)) {
            return null;
        }
        return LocalDateTime.ofEpochSecond(seconds, 0, ZoneOffset.ofHours(8));
    }

    /**
     * 将时间戳（毫秒）转为 LocalDateTime
     *
     * @param milliseconds 时间戳（毫秒）
     * @return LocalDateTime
     */
    public static LocalDateTime millsToLocalDateTime(Long milliseconds) {
        if (ValidUtil.isNull(milliseconds)) {
            return null;
        }
        return Instant.ofEpochMilli(milliseconds).atZone(ZoneOffset.ofHours(8)).toLocalDateTime();
    }

    public static Long getLastTime(Integer day) {
        Calendar c = new GregorianCalendar();
        Date date = new Date();
        c.setTime(date);
        c.add(Calendar.DAY_OF_WEEK, day);
        Date dateBefore = c.getTime();//两种把Calendar转化成Long类型的方法（毫秒）
        return dateBefore.getTime();
    }

    /**
     * LocalDateTime 转 Date
     */
    public static Date toDate(LocalDateTime localDateTime) {
        if (ObjectUtil.isEmpty(localDateTime)) return null;
        return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
    }

    /**
     * 获取yyyy-MM-dd时间
     *
     * @param localDate 时间
     * @return {@link String}
     */
    public static String ymdDate(LocalDate localDate) {
        if (null == localDate) {
            return null;
        }
        return localDate.format(FORMATTER_Y_M_D);
    }

    /**
     * 获取yyyy-MM-dd HH:mm
     *
     * @param localDateTime 时间
     * @return {@link String}
     */
    public static String ymdHmDate(LocalDateTime localDateTime) {
        if (ObjectUtil.isEmpty(localDateTime)) return null;
        return FORMATTER_Y_M_D_H_M.format(localDateTime);
    }

    public static String HmDate(LocalDateTime localDateTime) {
        if (ObjectUtil.isEmpty(localDateTime)) return null;
        return FORMATTER_H_M.format(localDateTime);
    }

    /**
     * yyyy-MM-dd转LocalDate
     *
     * @param time      时间
     * @param ofPattern 时间格式
     * @return {@link LocalDate}
     */
    public static LocalDate ymdToLocalDate(String time, DateTimeFormatter ofPattern) {
        if (StrUtil.isEmpty(time)) return null;
        return LocalDate.parse(time, ofPattern);
    }

    /**
     * yyyy-MM-dd HH:mm:ss转LocalDateTime
     *
     * @param time 时间
     * @return {@link LocalDate}
     */
    public static LocalDateTime ymdToLocalDateTime(String time) {
        if (StrUtil.isEmpty(time)) return null;
        return LocalDateTime.parse(time, FORMATTER_Y_M_D_H_M_S);
    }

    /**
     * @param dateString        自定义日期字符串
     * @param dateTimeFormatter 自定义与之匹配的日期格式
     */
    public static LocalDateTime stringToLocalDateTime(String dateString, DateTimeFormatter dateTimeFormatter) {
        if (StrUtil.isEmpty(dateString) || Objects.isNull(dateTimeFormatter)) return null;
        return LocalDateTime.parse(dateString, dateTimeFormatter);
    }

    /**
     * 获取两个日期时间的时间段
     * 如"2021.08.21 10:30~11:30"
     * 两个时间的日期相同，如不相同，取第一个日期时间为返回的日期值
     *
     * @param startDateTime     开始时间
     * @param endDateTime       结束时间
     * @param dateTimeFormatter 日期格式化
     * @param split             时间段分隔符号
     * @return 时间段
     */
    public static String dateTimeQuantum(LocalDateTime startDateTime, LocalDateTime endDateTime,
                                         DateTimeFormatter dateTimeFormatter, String split) {
        if (ValidUtil.isNull(startDateTime) || ValidUtil.isNull(endDateTime)) {
            return null;
        }
        String dateStr = startDateTime.toLocalDate().format(dateTimeFormatter);
        String timeQuantumStr = timeQuantum(startDateTime.toLocalTime(), endDateTime.toLocalTime(), split);
        return dateStr + StrUtil.SPACE + timeQuantumStr;
    }

    /**
     * 获取两个时间的时间段(时:分)
     * 如"10:30~11:30"
     *
     * @param startDateTime 开始时间
     * @param endDateTime   结束时间
     * @param split         时间段分隔符号
     * @return 时间段
     */
    public static String timeQuantum(LocalTime startDateTime, LocalTime endDateTime, String split) {
        if (ObjectUtil.isEmpty(startDateTime) || ObjectUtil.isEmpty(endDateTime)) return null;
        String startTime = startDateTime.format(FORMATTER_H_M);
        String endTime = endDateTime.format(FORMATTER_H_M);
        return startTime + split + endTime;
    }

    public static String formatDefault(Long milliSecond, String pattern) {
        if (Objects.isNull(milliSecond)) return StrUtil.EMPTY;
        return RConst.Num.LONG_0.equals(milliSecond) ?
                StrUtil.DASHED : format(longToLocalDateTime(milliSecond), pattern);
    }

    public static String format(Long milliSecond, String pattern) {
        if (Objects.isNull(milliSecond)) return StrUtil.EMPTY;
        return format(longToLocalDateTime(milliSecond), pattern);
    }

    public static String format(LocalDateTime dateTime, String pattern) {
        if (Objects.isNull(dateTime)) return StrUtil.EMPTY;
        return cn.hutool.core.date.DateUtil.format(dateTime, pattern);
    }

    /**
     * 获取本月的第一天或最后一天
     *
     * @param today   当前时间
     * @param isFirst [today, isFirst: true 表示开始时间，false表示结束时间]
     * @return {@link String}
     */
    public static String getStartOrEndDayOfMonth(LocalDate today, Boolean isFirst) {
        LocalDate resDate = LocalDate.now();
        if (today == null) {
            today = resDate;
        }
        Month month = today.getMonth();
        int length = month.length(today.isLeapYear());
        if (isFirst) {
            resDate = LocalDate.of(today.getYear(), month, 1);
        } else {
            resDate = LocalDate.of(today.getYear(), month, length);
        }
        return ymdDate(resDate);
    }

    /**
     * 获取今天星期几
     *
     * @return {@link Integer}
     */
    public static Integer week(LocalDateTime time) {
        if (Objects.isNull(time)) {
            return null;
        }
        String[][] strArray = {{"MONDAY", "1"}, {"TUESDAY", "2"}, {"WEDNESDAY", "3"}, {"THURSDAY", "4"}, {"FRIDAY", "5"}, {"SATURDAY", "6"}, {"SUNDAY", "7"}};
        String k = String.valueOf(time.getDayOfWeek());
        //获取行数
        for (String[] strings : strArray) {
            if (k.equals(strings[0])) {
                k = strings[1];
                break;
            }
        }
        return Integer.parseInt(k);
    }

}
